Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enable custom network signing #1444

Merged
merged 23 commits into from
Feb 20, 2024
Merged

Enable custom network signing #1444

merged 23 commits into from
Feb 20, 2024

Conversation

Trivo25
Copy link
Member

@Trivo25 Trivo25 commented Feb 18, 2024

This PR enables custom network signing and verification, all across our stack (o1js and mina-signer). This feature unlocks the use of o1js and mina-signer with layer 2s/3s or chains isomorphic to Mina. All while maintaining backwards compatibility

bindings o1-labs/o1js-bindings#247

closes #1149

@@ -159,18 +160,6 @@ function accountUpdatesToCallForest<A extends { body: { callDepth: number } }>(
return forest;
}

const zkAppBodyPrefix = (network: string) => {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

moved this into mina-signer/src/signature.js for shared use with o1js

expect(hashTestnet).toEqual(hashSnarkyTestnet.toBigInt());
expect(hashMainnet).toEqual(hashSnarkyMainnet.toBigInt());
});
test(
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cleaned this up a little by utilizing RandomTransaction.networkId

let commitment = callForestHash(callForest, 'testnet');
expect(commitment).toEqual(FieldConst.toBigint(ocamlCommitments.commitment));
});
test(
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this now tests all network id types

}
}

const createCustomPrefix = (prefix: string) => {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OCaml uses a * padding to 20 bytes

@@ -23,26 +23,29 @@ function checkConsistentSingle(
msg: Field,
key: PrivateKey,
keySnarky: PrivateKeySnarky,
pk: PublicKey
pk: PublicKey,
networkId: string
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

some cleanup, tests all three network cases now

@Trivo25 Trivo25 marked this pull request as ready for review February 18, 2024 14:03
@Trivo25 Trivo25 changed the title Enable custom network signing Enable custom network signing Feb 18, 2024
Copy link
Contributor

@MartinOndejka MartinOndejka left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Signature in o1js (not mina-signer) has still hard-coded testnet prefix.

@Trivo25
Copy link
Member Author

Trivo25 commented Feb 19, 2024

Signature in o1js (not mina-signer) has still hard-coded testnet prefix.

good catch!

@mitschabaude
Copy link
Member

mitschabaude commented Feb 19, 2024

Signature in o1js (not mina-signer) has still hard-coded testnet prefix.

good catch!

The network id was purposefully left out as a parameter in the userland signature methods: the in-SNARK Signature.verify() and its mina-signer counter-part signFields(). These signatures are not designed to be related to any particular network, and are not verified by a Mina node.

We only happen to reuse the same signing functions also used by transaction signatures, so we had to pick some choice of networkId. So we decided to hard-code "testnet".

Unless there is a specific reason to make these signatures network-specific in the PR, I'd be in favor of reverting these changes. Adding the networkId parameter just adds confusion IMO.

CHANGELOG.md Outdated Show resolved Hide resolved
src/lib/signature.ts Outdated Show resolved Hide resolved
src/mina-signer/mina-signer.ts Outdated Show resolved Hide resolved
@Trivo25
Copy link
Member Author

Trivo25 commented Feb 19, 2024

Unless there is a specific reason to make these signatures network-specific in the PR, I'd be in favor of reverting these changes. Adding the networkId parameter just adds confusion IMO.

Hmm given that you can specify a custom message anyway (with a custom nonce), this really can be reverted. But curious to know if @MartinOndejka sees an explicit need for this?

@MartinOndejka
Copy link
Contributor

Unless there is a specific reason to make these signatures network-specific in the PR, I'd be in favor of reverting these changes. Adding the networkId parameter just adds confusion IMO.

Hmm given that you can specify a custom message anyway (with a custom nonce), this really can be reverted. But curious to know if @MartinOndejka sees an explicit need for this?

Not at all, I just remember that it was a little confusing a while back when I found out that it has hardcoded testnet.

@mitschabaude
Copy link
Member

mitschabaude commented Feb 19, 2024

Not at all, I just remember that it was a little confusing a while back when I found out that it has hardcoded testnet.

@Trivo25 maybe it would be good to add a comment to these methods explaining why they hard-code testnet

@@ -9,7 +9,7 @@ export type Field = number | bigint | string;
export type PublicKey = string;
export type PrivateKey = string;
export type Signature = SignatureJson;
export type NetworkId = 'mainnet' | 'testnet';
export type NetworkId = string;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe something like export type NetworkId = 'mainnet' | 'testnet' | string; for better intellisense?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think TS would just collapse that to string, because 'mainnet' and 'testnet' are contained in string

Copy link
Member

@mitschabaude mitschabaude Feb 19, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

so after all, we can probably just remove the NetworkId export which caused us to release 2 or 3 new versions in a row 😆

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

btw, now we also need those runtime checks which I made you remove @shimkiv 😅

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

damn, I wonder if we should keep the detailed type somehow. Maybe something like

type NetworkId = 'mainnet' | 'testnet' | { custom: string }

@Trivo25 wdyt

expect(hashTestnet).toEqual(hashSnarkyTestnet.toBigInt());
expect(hashMainnet).toEqual(hashSnarkyMainnet.toBigInt());
});
test(
Copy link
Member

@shimkiv shimkiv Feb 20, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Trivo25
Throughout different tests we check that signatures match like testnet <-> testnet but should we also check that the resulting signatures are indeed different for different networks? Like testnet <-> mainnet should always differ.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, we should! And we do
For example, further down we verify against a different network and expect the verification to fail

expect(
   verify(sigFieldElements, networkId === 'mainnet' ? 'testnet' : 'mainnet')
).toEqual(false);

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

but I added a check that the hash doesnt match another networks hash!

if (!options?.network) {
throw Error('Invalid Specified Network');
}
const specifiedNetwork = options.network.toLowerCase();
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is no longer needed since the type now is 'testnet' | 'mainnet' which already is lower case

expect(hashTestnet).toEqual(hashSnarkyTestnet.toBigInt());
expect(hashMainnet).toEqual(hashSnarkyMainnet.toBigInt());
});
test(
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

but I added a check that the hash doesnt match another networks hash!

expect(okMainnetTestnet).toEqual(false);
expect(okTestnetMainnet).toEqual(false);
expect(okMainnetMainnet).toEqual(true);
expect(verifyFieldElement(sig, msg, pk, networkId)).toEqual(true);
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

test is now condensed by using networkId as input

export type NetworkId = 'mainnet' | 'testnet';
export type NetworkId = 'mainnet' | 'testnet' | { custom: string };

export const NetworkId = {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

internal helper to get the plain networkId

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nicely done!

Copy link
Member

@mitschabaude mitschabaude left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

awesome!

@Trivo25 Trivo25 merged commit b0fb1c8 into main Feb 20, 2024
13 checks passed
@Trivo25 Trivo25 deleted the custom-network branch February 20, 2024 18:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

mina-signer: use string instead of boolean for compilation option
4 participants